using System;
using System.IO;
#if !XBOX
using System.Reflection;
#endif
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Oops.Xna.Framework.Input;

namespace Oops.Xna.Framework.Diagnostics
{
	/// <summary>
	/// Provides the ability to take screenshots of the current frame by pressing the specified keyboard key.
	/// </summary>
	public class ScreenshotComponent : DrawableGameComponent
	{
		private IInputService inputService;

		private bool captureScreen;

		/// <summary>
		/// Initializes a new instance of the <see cref="ScreenshotComponent"/> class.
		/// </summary>
		/// <param name="game">The <see cref="Microsoft.Xna.Framework.Game"/>that the game component should be attached to.</param>
		public ScreenshotComponent(Game game) : base(game)
		{
			// The game parameter must be specified.
			if (game == null)
			{
				throw new ArgumentNullException("game");
			}
#if !XBOX
			// Initialize the path.
			this.Path = System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
#endif
			// Default some property values.
			this.CaptureKey = Keys.PrintScreen;
			this.FileName = "Screenshot {0:0000}";
		}

		/// <summary>
		/// Gets or sets the <see cref="Microsoft.Xna.Framework.Input.Keys"/> to query for a screenshot.
		/// </summary>
		public Keys CaptureKey { get; set; }
	    
		/// <summary>
		/// Gets or sets the location to save the screenshots.
		/// </summary>
		public string Path { get; set; }
	    
		/// <summary>
		/// Gets or sets the filename mask to use when saving a screenshot.
		/// </summary>
		public string FileName { get; set; }
#if !XBOX
		/// <summary>
		/// Gets or sets the file format to use when saving a screenshot.
		/// </summary>
		public ImageFileFormat FileFormat { get; set; }
#endif
		#region IGameComponent Members

		public override void Initialize()
		{
			// Get the input service.
			this.inputService = this.Game.GetService<IInputService>();
			
			// Call inherited method.
			base.Initialize();
		}

		#endregion

		#region IUpdateable Members

		public override void Update(GameTime gameTime)
		{
			// Determine if the screen shot request occurred.
			this.captureScreen = this.inputService.IsKeyPressed(this.CaptureKey);

			// Call inherited method.
			base.Update(gameTime);
		}

		#endregion

		#region IDrawable Members

		public override void Draw(GameTime gameTime)
		{
			if (this.captureScreen)
			{
#if !XBOX
				// Using the path, determine the index of the screenshot.
				int index = 1;
				// TODO : GC!
				string fileName = System.IO.Path.Combine(this.Path, string.Format(this.FileName, index)) + "." + this.FileFormat.ToString().ToLower();
				while (File.Exists(fileName))
				{
					// TODO : GC!
					fileName = System.IO.Path.Combine(this.Path, string.Format(this.FileName, ++index)) + "." + this.FileFormat.ToString().ToLower();
				}

				// Create the texture that the back buffer will be resolved to.
				// TODO : GC!
				ResolveTexture2D texture = new ResolveTexture2D(
					this.GraphicsDevice,
					this.GraphicsDevice.PresentationParameters.BackBufferWidth,
					this.GraphicsDevice.PresentationParameters.BackBufferHeight,
					1, 
					SurfaceFormat.Color);
	                
				// Resolve the back buffer and save the texture to the specified file.
				using (texture)
				{
					this.GraphicsDevice.ResolveBackBuffer(texture);
					texture.Save(fileName, ImageFileFormat.Bmp);
				}
#endif
				// Reset the capture flag.
				this.captureScreen = false;
			}
		}

		#endregion
	}
}